#include "Common.h"
#include "Headers.h"
#include "Platform.h"


#ifdef _DEBUG
#define DEVICE_CREATE_FLAG XAUDIO2_DEBUG_ENGINE
#else
#define DEVICE_CREATE_FLAG 0 
#endif


namespace Audio
{
	class LocalLocker
	{
	public:
		LocalLocker(ILocker* l)
			:locker(l)
		{
			locker->Lock();
		}

		~LocalLocker()
		{
			locker->Unlock();
		}

	private:
		ILocker* locker;
	};

	template<typename T>
	class EnsureReleasePointer;

	template<typename T>
	class EnsureReleasePointer<T*>
	{
	public:
		typedef T  non_pointer_type;
		typedef T* pointer_type;
		EnsureReleasePointer(pointer_type t):ptr(t){}
		~EnsureReleasePointer(){ delete ptr;}

		pointer_type operator->(){ return ptr;}
		operator bool(){ return ptr != NULL; }
		operator T*(){ return ptr; }
	private:
		EnsureReleasePointer(const EnsureReleasePointer& other);
		EnsureReleasePointer& operator=(const EnsureReleasePointer& other); 
		pointer_type ptr;
	};

	

	AudioManager::AudioManager()
		:device(NULL)	
		,locker(NULL)
		,triggerEvent(NULL)
		,workingThread(NULL)
		,factory(NULL)
		,engineCallback(NULL)
	{		
		Init();
	}

	void AudioManager::Init()
	{
		factory = new WindowsPlatform::WindowsFactory();

		locker =		factory->CreateLocker();
		triggerEvent =	factory->CreateEventObject();
		workingThread = factory->CreateThread();
		
		device        = factory->CreateAudioDevice();
		engineCallback = new AudioManagerCallback(this);
		device->SetDeviceCallback(engineCallback);

		workingThread->Start(this);

		
		SendCommand(new InitDeviceCommand());
	}

	void AudioManager::Destroy()
	{
		SendCommand(new EndAudioThreadCommand());

		factory->DestroyEventObject(triggerEvent);
		factory->DestroyLocker(locker);

		workingThread->Stop();
		workingThread->Join();
		factory->DestroyThread(workingThread);

		while(!commands.empty())
		{
			AudioCommand* command = commands.front();
			commands.pop();
			delete command;
		}
		delete engineCallback;
	}


	AudioManager::~AudioManager()
	{		
		Destroy();		
	}		

	void AudioManager::Run()
	{
		try
		{				
			do 
			{
				triggerEvent->WaitOn();				
				ProcessCommands();		
				
			} while (!workingThread->HasStopRequest());
		}		
		catch(const ExceptionBase& e)
		{
			
		}			
	}

	UINT32 AudioManager::ReturnCode()
	{
		return 0;
	}

	void AudioManager::ProcessCommands()
	{
		LocalLocker l(locker);
		while(!commands.empty())
		{
			EnsureReleasePointer<AudioCommand*> command = commands.front();
			commands.pop();
			if(command)
			{				
				command->Do(device);							
			}
		}
	}

	AudioVoice* AudioManager::CreateVoice(const String& name)
	{
		AudioVoice* voice = NULL;
		try
		{
			voice = new AudioVoice(name);			
			SendCommand(new CreateVoiceCommand(voice));
		}
		catch (const ExceptionBase& e)
		{
			delete voice;
		}	
		return voice;
	}

	void AudioManager::DestroyVoice(AudioVoice* voice)
	{
		SendCommand(new DestroyVoiceCommand(voice));
	}

	void AudioManager::Play(AudioVoice* voice)
	{
		SendCommand(new PlayCommand(voice));
	}

	void AudioManager::Stop(AudioVoice* voice)
	{
		//SendCommand(new StopCommand(voice));
	}

	void AudioManager::SendCommand(AudioCommand* command)
	{
		LocalLocker l(locker);
		{
			commands.push(command);
		}		
		triggerEvent->Signal();
	}	
	

	void AudioManagerCallback::OnNeedMoreData(void* context)
	{
		if(mgr != NULL)
		{
			AudioPlayingVoice* voice = static_cast<AudioPlayingVoice*>(context);
			if(voice)
				mgr->SendCommand(new LoadDataCommand(voice));
		}
	}
	void AudioManagerCallback::OnStreamEnd(void* context)
	{
		
	}
	void AudioManagerCallback::OnVoiceError(void* context)
	{

	}

	

}
